home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Mania 6
/
MacMania 6.toast
/
/
Multimedia & Desktop
/
VideoToolbox
/
VideoToolboxSources
/
Timer.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-05-30
|
26KB
|
640 lines
/*
Timer.c
Don't use Timer.c! Use Seconds.c instead.
NOTE: Fran Miezin, fran@petcn.wustl.edu, writes (2/97) to report that repeatedly
calling PeekTimer in a tight loop causes the Mac's clock to run fast, resulting
in timing that is about 2% different from that measured with a stopwatch. On
a 7100 (System 7.5.3) Fran found that calling PeekTimer distorted the times
reported by Timer.c while TickCount was unaffected, but on a 7500 the
TickCount rate was affected as well. My conclusion is that this represents yet
another bug in Apple's Time Manager.
Don't use it. Don't use this VideoToolbox Timer.c file, since it's based on
Apple's Time Manager traps. Use the VideoToolbox Seconds.c instead;
it's much easier to use, bug-free, and has no side effects.
NOTE: I just learned (6/95) of a handy Apple Toolbox routine called Microseconds(),
which appeared with System 7, and is documented in Inside Mac: OS Utilities,
and in Minow's article in Develop,"Timing on the Macintosh"
<http://devworld.apple.com/dev/techsupport/develop/issue26/minow.html>
For most applications Microseconds() will be easier to use than Timer.c,
and just as accurate. See the demo NoiseVBL.c for an example of its use.
1/97: In fact, there's an even better routine, UpTime, available on some machines.
I recommend you just call the new VideoToolbox Seconds.c which uses the best
available time routine.
Timer.c is an interval timer based on Apple's Time Manager. It returns the time, in
seconds, that elapsed between calling StartTimer() and PeekTimerSecs() or
StopTimerSecs(). The timing of a single interval (from StartTimer to PeekTimer,
PeekTimerSecs, StopTimer, or StopTimerSecs) is always very accurate (better than
1 part in 1000 relative to a good time standard) and a precision of better than
200 µs. Under System 7, which has the Extended Time Manager, (except on the
Radius Rocket) this accuracy applies to all intervals; measuring splits by
peeking has no effect on the accuracy of subsequent times. Under System 6 (or on
the Radius Rocket even under System 7) each peek will lose a variable amount of
time, compromising accuracy of subsequent times from that timer, due to
documented deficiencies of Apple's old Revised Time Manager. The even older
"Standard" Time Manager is not supported here, and will result in an error
message. Note that you are allowed an unlimited number of timers that each
operate independently. Peeking one timer has no effect on the rest of your
timers.
Timer *NewTimer(void);
void DisposeTimer(Timer *t);
void StartTimer(Timer *t);
double PeekTimerSecs(Timer *t); // s, with µs precision and no time limit
double StopTimerSecs(Timer *t); // s, with µs precision and no time limit
long PeekTimer(Timer *t); // µs, up to 36 minutes
long StopTimer(Timer *t); // µs, up to 36 minutes
Timer *timer;
long t;
timer=NewTimer();
StartTimer(timer);
do(i=0;i<100;i++);
t=StopTimer(timer);
DisposeTimer(timer);
printf("One hundred iterations takes %ld µs\n",t);
The timing result comes in two flavors. StopTimerSecs() and PeekTimerSecs() both
return the time in secs as a double, and can time an interval of essentially
unlimited duration with 200 µs precision. StopTimer() and PeekTimer() return the
time in microseconds (with same 200 µs precision) as a long, which can hold a
time up to 2,147,483,647 µs, which is nearly 36 minutes. I generally prefer
PeekTimerSecs, because it's foolproof (won't overflow), even though it's a bit
slower than PeekTimer. But if you don't have a floating point unit, then
PeekTimerSecs is MUCH slower than PeekTimer.
You can have an unlimited number of Timers running at once. The only restriction
is that you must create each Timer, by calling NewTimer() before you use it,
and, obviously, should not use it after calling DisposeTimer().
The Time Manager seems to use interrupts at a higher rate than the 10 s interval
I requested here (which would never expire in typical use). I infer this from
the fact that disabling interrupts by SetPriority(7) greatly reduces the timing
value returned by StopTimer(). So don't disable interrupts while you're timing.
StopTimer() adds a small offset (about 62 µs on a Mac IIci) to the raw time in
order to return an unbiased estimate of the time from when StartTime was called
to when StopTime() was called. This is the most useful time measure for
measuring the interval between two events (e.g. video frames). Alternatively, if
you wish to measure processing time, then it is more useful to compute the time
from when StartTimer() returns to when StopTimer() is called. For this you
simply subtract the fixed duration of StartTimer(), from call to return, which is
provided in µs in your Timer structure in the long structure member
"timeToStartTimer".
betweenTime = StopTimer(t) - t->timeToStartTimer;
When you use StopTimerSecs() you'd have to divide timeToStartTimer by a
million to convert µs to s. For some purposes this offset is negligibly small.
On my Mac IIci the timeToStartTimer is 240 µs. This offset, and the one
mentioned above are measured automatically the first time you call NewTimer().
Alternatively, instead of having to remember the name of the structure member,
just measure the timeToStartTimer yourself and subtract it from all subsequent
measures:
StartTimer(t);
s0=StopTimerSec(t);
The Timer structures are kept in a linked list so that KillEveryTimer(), which
is placed in the _atexit() queue, will find and kill them when the application
exits.
TEST PROGRAM:
Measure the same interval in three different ways. All three answers agree, at
least under System 7. Note that timer2 is repeatedly peeked at, while "timer" is
uninterrupted. This checks for any side effects of peeking. (There will be a
substantial loss of time due to peeking if you run without the Extended Time Mgr,
i.e. under System 6 or on Radius Rocket.)
void main(void)
{
double s,s2;
long ticks,m,m2;
int i,n=1000;
Timer *timer,*timer2;
printf("We'll simultaneously measure a single time interval in 3 ways:\n"
"1. by just starting and stopping the timer\n"
"2. by starting the timer and repeatedly peeking at it %d times\n"
"3. by using TickCount(), which runs at 60.15 Hz.\n...\n",n);
timer=NewTimer();
timer2=NewTimer();
Delay(1,&ticks); // synchronize ourselves to the tick counter for better accuracy.
StartTimer(timer);
StartTimer(timer2);
for(i=0;i<n;i++)s2=PeekTimerSecs(timer2); // time PeekTimerSecs()
ticks=TickCount()-ticks;
s=StopTimerSecs(timer);
s2=StopTimerSecs(timer2);
printf("The interval was:\n1. %4.0f ms\n2. %4.0f ms\n3. %4.0f±%.0f ms\n\n"
,s*1e3,s2*1e3,1e3*(ticks+0.5)/60.15,1e3*0.5/60.15);
printf("Each call to PeekTimerSecs() takes %4.0f µs, and loses %4.0f µs.\n"
,s*1e6/n,(s-s2)*1e6/n);
StartTimer(timer);
StartTimer(timer2);
for(i=0;i<n;i++)m2=PeekTimer(timer2); // time PeekTimer()
m=StopTimer(timer);
m2=StopTimer(timer2);
printf("Each call to PeekTimer() takes %4ld µs, and loses %4ld µs.\n"
,m/n,(m-m2)/n);
DisposeTimer(timer);
DisposeTimer(timer2);
}
ACKNOWLEDGMENT:
David Brainard wrote the first draft of PeekTimer().
APPLE DOCUMENTION ERRORS:
9/6/96. I've just learned from Eric Simenel at Apple, that these two points
will be addressed, authoritatively, in his Tech Note 1063.
<http://devworld.apple.com/dev/technotes/tn/tn1063.html>
There are two errors in the Time Manager chapter of "New Inside
Macintosh: Processes". One of the errors is particularly relevant to the
PowerPC. Both errors are on page 3-8.
1. The manual says, "You should set tmWakeUp and tmReserved to 0 when you first
install an extended Time Manager task." That's correct, but it should go on to
say that "You should set tmReserved to 0 before each and every call to
InsXTime." (This is now confirmed by Apple, see 7/31/96 note below from DEVSUPPORT.)
It took me a long time to discover this. Some computers, e.g. my PowerBook 170,
don't care. But, e.g. the PowerMac 6100, does care. It increments tmReserved
each time a task is installed by InsXTime. If tmReserved reaches 128 then the
Time Manager goes haywire. Conversely, everything runs fine on every Mac I've
tested if tmReserved is zeroed before each call to InsXTime.
2. The manual says "The tmWakeUp field contains the time at which the Time
Manager task specified by tmAddr was last executed (or 0 if it has not yet been
executed)." THIS IS WRONG AND MISLEADING! It should say, instead, "The tmWakeUp
field contains the time at which the Time Manager task specified by tmAddr is
scheduled to be executed (or 0 if it has not yet been scheduled)."
Subject: Apple DTS acks bug you documented in Timer.c
From: Sigurdur Asgeirsson, sigurasg@menandmice.com
To: denis@psych.nyu.edu
While implementing a fixed frequency Time Manager Task in my program I
came across a situation where my internal time would periodically pass very
very quickly. Remembering that there was some kind of timer service in the
VideoToolBox sources, I grabbed them off the net (after a three day
debugging session, mind you :-(), and had a look at your code. I then
modified my code to set tmReserved to 0 before every InsXTime and wrote to
DTS. What follows is their response, I'm not sure if this message may be
redistributed without Mr. Eric Simenel's prior consent, but at least here
is confirmation on the Time Manager bug.
Date: 31 Jul 1996
From: "DEVSUPPORT" <devsupport@apple.com>
To: "Apple Developer" <DEVELOPER@MENANDMICE.COM>
Reference #191859
Hello Sigurdur,
You're right and Denis G. Pelli, the author of Video-Toolbox is right too.
Back in October '92, a modification was done to the Time Mgr and has never
been documented...
I'm not authorized to divulge too many details but I can say this:
• This non-documented modification has been running since October '92 and
it has never been modified ever since, so I'd say that it's here to stay a
long time (I can't guarantee FOREVER since that's too long a time...).
• After 127 InsXTime/PrimeTime calls, the Extended Time Task is converted
in a non-extended Time Task, and since it is being called with a 0 delay,
it's no surprise your internal time seems to pass very very quickly...
• If you set tmReserved to 0 before each InsXTime, this never happens.
In fact, the InsXTime call doesn't do a thing with tmReserved, it's
PrimeTime which deals with it, and the modification is in PrimeTime.
Since it has never been documented before (except by Denis G. Pelli), I
believe I will write a Tech Note about it. Thank you for signaling this to
us.
Eric Simenel
Developer Support Center
RADIUS ROCKET BUG:
This applies to my Radius Rocket in my Mac II using RocketWare 1.5 running under
System 7.1 with System Update.
Apparently Radius decided not to implement the Extended Time Manager and only
went so far as diverting the InsXTime trap to InsTime. A minimal fix would be to
at least give honest information by making Gestalt correctly report the Time
Manager version as gestaltRevisedTimeMgr instead of falsely claiming it's
gestaltExtendedTimeMgr. My programs check for this, and I spent a whole day
debugging crashes on my Radius due to this misleading information from Gestalt.
According to Gestalt, the Extended Time Manager is present, but in fact the
InsXTime trap is only providing the service expected from the InsTime trap of
the Revised Time Manager. Specifically, in the program below, when we call
InsXTime the second time, with a nonzero tmWakeUp, we are supposed to resume the
previous interval, not start a new one. Since the original interval was 100 s,
there should still be lots of time left in it, and when we call RmvTime for the
last time tmCount should be nonzero. This program fails only on my Radius
Rocket. It runs fine on my Mac II, IIci, IIfx, LC475, Quadra 840av,
PowerBook 170, and PowerMac.
UNDERSTANDING THE CODE OF TIMER.C:
There are a few subtleties to understanding the code. Firstly, you should read
the manual, i.e. the Time Manager chapter of New Inside Mac: Processes. If the
Extended Time Manager is available, PeekTimer takes advantage of its ability to
resume the interrupted interval, so the splits (from PeekTimer and
PeekTimerSecs) won't have any effect on the accuracy of subsequent times.
However, there is a tricky case that requires special handling. If we are very
close to the end of the current interval then the interval might expire before
we have a chance to call PrimeTime(). As explained in the manual on page 3-8
this would amount to an impossible request for a task execution time in the
past. When that happens the Time Mgr changes the the requested time to the
present, which would destroy our synchrony. Therefore PeekTimer checks that
there is at least 10 ms left in the interrupted interval, and, if not, then
advances to the next interval, incrementing the counters appropriately as though
our timer task had been called. This transparently preserves accuracy.
HISTORY:
8/19/92 dgp based on Time Manager chapter in Inside Mac VI and TimeIt.c,
which is now obsolete. I also benefited from examining code by Jonothan Kolodny
forwarded to me by Thomas Busey.
8/27/92 dgp Rewrote everything. Made the interrupt service routine reentrant
by eliminating all use of global variables, using only the structure pointed
to by A1. There can now be an unlimited number of timers active at once.
Added NewTimer() and DisposeTimer() to manage them.
9/10/92 dgp added calls to VM to HoldMemory() and UnHoldMemory(). According to Apple's
Memory book this isn't strictly necessary, since Time Manager tasks will be
called only when it's safe.
1/11/93 dgp StopTimerSecs() now returns NAN if called with a NULL pointer.
7/9/93 dgp Test MATLAB in if() instead of #if.
5/28/94 dgp Made compatible with Apple's Universal Headers. Thanks to Bob Dougherty
(wolfgang@cats.ucsc.edu) for reporting the incompatibility.
9/7/94 dgp Made declaration of TimerTask--how it receives the argument ptr--conditional on PowerPC.
10/22/94 dgp Added PeekTimer, written by David Brainard, and enhanced it to used the Extended
Time Mgr's facility of resuming an interrupted interval. Modularized all the code somewhat,
eliminating duplications.
10/24/94 dgp Made declaration of GetA0() explicitly indicate that answer is returned in D0,
for better compatibility with Metrowerks CodeWarrior C.
10/27/94 dgp It is very annoying for the machine to crash anytime you try to quit your application
by escaping via MacsBugs escape to shell. The problem is that CodeWarrior 4.5 doesn't attach
the atexit() tasks to _EscapeToShell. So I do it here.
11/3/94 dgp In response to a bug report by David Brainard that after 128 calls to PeekTimer things
got screwy I replicated the problem and discovered that I could cure it by clearing tmReserved
in ResumeTimer below, though I have no idea why this is necessary.
11/4/94 dgp After discovering that the Radius Rocket lies, via Gestalt, claiming that
the Extended Time Manager is present when it really isn't, I added code to check for
the services of the Extended Time Manager, and adjust t->timeManagerVersion accordingly.
This eliminates the crashes I was getting when running PeekTimer on the Radius Rocket.
11/5/94 dgp tested and debugged the code when running without the Extended Time Manager.
1/5/95 dgp made compatible with Universal Headers 2.
5/3/95 dgp fixed test for presence of VM, which before was always returning false.
6/6/95 dgp in response to report from David Brainard, PeekTimer now returns LONG_MIN, like
StopTimer(), when supplied a NULL pointer.
7/1/95 dgp just call AtExitToShell(), without any conditionals. Special cases, e.g. MATLAB,
are handled in AtExitToShell.c.
9/5/96 dgp added Apple's confirmation, forwarded by Sigurdur Asgeirsson, sigurasg@menandmice.com,
of the undocumented need to keep zeroing the tmReserved field.
5/2/97 dgp Save/restore A4/5 as appropriate. Our timer task doesn't in fact use globals, so this
is superfluous, but I thought I'd add the code now, since I just figured out how to do it.
*/
#include "VideoToolbox.h"
#if GENERATINGPOWERPC
static pascal void TimerTask(Timer *t);
#else
static pascal void TimerTask(void);
#endif
void KillEveryTimer(void);
#define CODE_RESOURCE MATLAB
/* This is a copy for reference. Original is in VideoToolbox.h.
struct Timer{
TMTask time;
long ourA5;
long interval; // interval length (in negative µs)
long elapsed; // extra time, (in -µs), beyond elapsedIntervals*interval
long elapsedIntervals; // positive count
long timeToStartTimer; // minimum time in µs
long stopDelay; // µs from call to stop, re from call to start
long timeManagerVersion;
struct Timer *next,*previous; // doubly linked list of Timers
};
*/
static Timer defaultTimer,qTimer;
static long vmPresent=0,pageSize=0;
#define TASK_SIZE 1024 // Generous guess for size of routine
static double TimerSecs(Timer *t);
static void ResumeTimer(Timer *t);
static long TimerMicroSecs(Timer *t);
#if !defined(NewTimerProc)
#define NewTimerProc(proc) (TimerProcPtr)proc
#endif
Timer *NewTimer(void)
{
static short firstTime=1;
Timer *t,*tt;
long j;
if(firstTime){
firstTime=0;
qTimer.next=qTimer.previous=NULL;
AtExitToShell(KillEveryTimer);
Gestalt(gestaltVMAttr,&vmPresent);
vmPresent &= 1L<<gestaltVMPresent;
if(vmPresent)Gestalt(gestaltLogicalPageSize,&pageSize);
t=&defaultTimer;
#if GENERATING68K
#if CODE_RESOURCE
t->ourA5 = GetA4();
#else
t->ourA5 = SetCurrentA5();
#endif
#endif
t->time.tmAddr = NewTimerProc(TimerTask);
t->time.tmCount=t->time.tmWakeUp=t->time.tmReserved=0;
t->elapsedIntervals=t->elapsed=0;
t->timeManagerVersion=0;
Gestalt(gestaltTimeMgrVersion,&t->timeManagerVersion);
if(t->timeManagerVersion>=gestaltExtendedTimeMgr){
// Let's make sure the Extended Time Manager is really here.
// Under System 7.1 the Radius Rocket claims it's present, but it's not.
TMTask time;
time.tmAddr = NULL;
time.tmCount=time.tmWakeUp=time.tmReserved=0;
InsXTime((QElemPtr)&time);
PrimeTime((QElemPtr)&time,-100000000L); // interval of 100 s
RmvTime((QElemPtr) &time);
time.tmReserved=0; // undocumented, but necessary on some Macs
InsXTime((QElemPtr)&time);
PrimeTime((QElemPtr)&time,0); // resume interrupted interval
RmvTime((QElemPtr) &time);
// a tmCount of zero couldn't happen if the Extended Time Manager were present.
if(time.tmCount==0)t->timeManagerVersion=gestaltRevisedTimeMgr;
}
switch(t->timeManagerVersion){
case 0:
case gestaltStandardTimeMgr:
printf("NewTimer: old System lacks the Revised Time Manager. %s\n",__FILE__);
return NULL;
case gestaltRevisedTimeMgr:
case gestaltExtendedTimeMgr:
default:
// The programs in Timer.c assume that t->interval<0
t->interval = -10000000; // Set the timer interval to 10 s
}
t->next=NULL;
t->previous=&qTimer;
t->timeToStartTimer=t->stopDelay=0;
// Measure timeToStartTimer and stopDelay offsets.
t=NewTimer();
StartTimer(t);
t->timeToStartTimer=StopTimer(t);
tt=NewTimer();
StartTimer(t);
StartTimer(tt);
j=StopTimer(t);
DisposeTimer(tt);
t->stopDelay=2*t->timeToStartTimer-j;
// The computed "cycle" interval will have stopDelay removed.
// The user wishing to compute the "between" interval will be
// subtracting the timeToStartTimer, so we should subtract
// the stopDelay from that so the stopDelay cancels out when
// the "between" time is computed.
t->timeToStartTimer-=t->stopDelay;
defaultTimer.timeToStartTimer=t->timeToStartTimer;
defaultTimer.stopDelay=t->stopDelay;
return t;
}
t=(Timer *)NewPtr(sizeof(Timer));
if(t!=NULL){
*t=defaultTimer;
t->next=qTimer.next;
if(t->next!=NULL)t->next->previous=t;
qTimer.next=t;
if(vmPresent){
HoldMemory(t,sizeof(*t));
if(t->time.tmAddr!=NULL)HoldMemory(t->time.tmAddr,TASK_SIZE);
}
if(t->timeManagerVersion>=gestaltExtendedTimeMgr)InsXTime((QElemPtr)t);
else InsTime((QElemPtr)t);
}
return t;
}
void DisposeTimer(Timer *t)
{
if(t==NULL)return;
RmvTime((QElemPtr)t);
if(vmPresent){
//UnholdMemory(t,sizeof(*t));
if(qTimer.next==t && t->next==NULL && t->time.tmAddr!=NULL)
UnholdMemory(t->time.tmAddr,TASK_SIZE);
}
t->previous->next=t->next;
if(t->next!=NULL)t->next->previous=t->previous;
DisposPtr((Ptr)t);
}
void StartTimer(Timer *t)
{
if(t==NULL)return;
if(t->time.qType<0)StopTimer(t); // whoops, it's already active.
PrimeTime((QElemPtr)t,t->interval);
}
static void ResumeTimer(Timer *t)
{
// As noted above, it is important to clear tmReserved.
// The Time Manager documentation only says that tmReserved should be zeroed
// when you FIRST install your task, not EACH TIME you install it.
// At least some computers (e.g. PowerMac) increment tmReserved each time PeekTimer
// is called, and things get screwy if tmReserved>=128.
t->time.tmReserved=0;
if(t->timeManagerVersion>=gestaltExtendedTimeMgr){
// The Extended Time Mgr (System 7) allows us to resume exactly where we left off
// provided we don't clear t->time.tmWakeUp
if(t->time.tmCount==0 || t->time.tmCount<0 && t->time.tmCount>-10000){
// we're dangerously close to the end of this interval: skip to next interval
t->elapsedIntervals++;
InsXTime((QElemPtr)t);
PrimeTime((QElemPtr)t,t->interval); // resume, skipping current interval's wakeup
}else{
InsXTime((QElemPtr)t);
PrimeTime((QElemPtr)t,0); // resume interrupted interval
}
}else{
// Before System 7 we must start a new interval
t->elapsed+=t->interval;
if(t->time.tmCount<0)t->elapsed-=t->time.tmCount;
else t->elapsed+=t->time.tmCount*1000;
if(t->elapsed<t->interval){
// avoid overflow of t->elapsed
t->elapsed-=t->interval;
t->elapsedIntervals++;
}
t->time.qType=t->time.tmReserved=t->time.tmWakeUp=t->time.tmCount=0;
InsTime((QElemPtr)t);
PrimeTime((QElemPtr)t,t->interval); // resume timing
}
}
static double TimerSecs(register Timer *t)
{
register double s;
// add up the elapsed time plus the interval we're in, minus the time left in it
s=-((double)(t->elapsedIntervals+1)*t->interval + t->elapsed);// µs
if(t->time.tmCount>0) s-=t->time.tmCount*1000; // -µs until end of interval
else s+=t->time.tmCount; // -µs until end of interval
s-=t->stopDelay; // compute "cycle" time
s*=0.000001; // µs to s
return s;
}
static long TimerMicroSecs(register Timer *t)
{
register long microSecs;
// add up the elapsed time plus the interval we're in, minus the time left in it
microSecs=-((t->elapsedIntervals+1)*t->interval + t->elapsed);// µs
if(t->time.tmCount>0) microSecs-=t->time.tmCount*1000; // -µs until end of interval
else microSecs+=t->time.tmCount; // -µs until end of interval
microSecs-=t->stopDelay; // compute "cycle" time
return microSecs;
}
double PeekTimerSecs(Timer *t)
{
Timer tCopy;
if(t==NULL)return NAN;
RmvTime((QElemPtr)t);
tCopy=*t; // get the info and resume timing as quickly as possible
ResumeTimer(t);
return TimerSecs(&tCopy);
}
long PeekTimer(Timer *t)
{
Timer tCopy;
if(t==NULL)return LONG_MIN;
RmvTime((QElemPtr)t);
tCopy=*t; // get the info and resume timing as quickly as possible
ResumeTimer(t);
return TimerMicroSecs(&tCopy);
}
long StopTimer(Timer *t) // Returns µs
{
long microSecs;
if(t==NULL)return LONG_MIN;
RmvTime((QElemPtr)t);
microSecs=TimerMicroSecs(t);
// Reinstall the Timer, to be ready for for another call to StartTimer()
t->time.qType=t->time.tmCount=t->time.tmWakeUp=t->time.tmReserved=0;
t->elapsed=t->elapsedIntervals=0;
if(t->timeManagerVersion>=gestaltExtendedTimeMgr)InsXTime((QElemPtr)t);
else InsTime((QElemPtr)t);
return microSecs;
}
double StopTimerSecs(Timer *t) // Returns secs
{
double s;
if(t==NULL)return NAN;
RmvTime((QElemPtr)t);
s=TimerSecs(t);
// Reinstall the Timer, to be ready for for another call to StartTimer()
t->time.qType=t->time.tmCount=t->time.tmWakeUp=t->time.tmReserved=0;
t->elapsed=t->elapsedIntervals=0;
if(t->timeManagerVersion>=gestaltExtendedTimeMgr)InsXTime((QElemPtr)t);
else InsTime((QElemPtr)t);
return s;
}
// KillEveryTimer turns off all our Timers before we quit. There is no need to
// free the space since the system will do that automatically when the application terminates.
void KillEveryTimer(void)
{
Timer *t;
extern Timer qTimer;
t=qTimer.next;
while(t!=NULL){
RmvTime((QElemPtr)t);
if(vmPresent){
UnholdMemory(t,sizeof(*t));
if(t->time.tmAddr!=NULL)UnholdMemory(t->time.tmAddr,TASK_SIZE);
}
t=t->next;
}
}
// On 68k machines the Revised & Extended Time managers set A1=&task.time before calling TimerTask
// The code allowing access to globals is commented out because it is not needed here.
#if (THINK_C || THINK_CPLUS || SYMANTEC_C)
#pragma options(!profile) // it would be dangerous to call the profiler from here
#endif
#if __MWERKS__
#pragma profile off // on 68k it would be dangerous to call the profiler from here
#endif
#if GENERATING68K
#pragma parameter __D0 GetA1()
long GetA1(void)=0x2009; // MOVE.L A1,D0
#if THINK_C
#pragma parameter __D0 SetA4(__D0)
long SetA4(long) = 0xC18C; // EXG D0,A4
#else
long SetA4(long:__D0):__D0 = 0xC18C; // EXG D0,A4
#endif
#endif
#if GENERATINGPOWERPC
static pascal void TimerTask(Timer *t)
#else
static pascal void TimerTask(void) // Called at interrupt time
#endif
{
#if GENERATING68K
long oldA;
Timer *t;
t=(Timer *)GetA1();
#if CODE_RESOURCE
oldA = SetA4(t->ourA5); // Reestablish A4 for global variables
#else
oldA = SetA5(t->ourA5); // Reestablish A5 for global variables
#endif
#endif
PrimeTime((QElemPtr)t,t->interval); // Repeat the interval
t->elapsedIntervals++;
#if GENERATING68K
#if CODE_RESOURCE
SetA4(oldA); // Restore A4
#else
SetA5(oldA); // Restore A5
#endif
#endif
}